﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System.Runtime.Serialization;
using System;
using System.ComponentModel;
using System.Threading;
#if USE_HYPER
using Hyper.ComponentModel;
#endif

namespace BrainTechLLC.ThreadSafeObjects
{
    /// <summary>
    /// Caches an object or value of type T
    /// </summary>
#if NO_SILVERLIGHT
    [TypeDescriptionProvider(typeof(HyperTypeDescriptionProvider))]
#endif
    [Serializable]
    [DataContract]
    public class Cached<T> : GenericCacheBase<T>, ICacheBase<T>
    {
        public Cached(TPopulateCacheDelegate<T> populateCacheDelegate) : base(populateCacheDelegate) { }
        public Cached(TPopulateCacheDelegateDirect<T> populateCacheDelegate) : base(populateCacheDelegate) { }
        public Cached() { }

        /// <summary>
        /// Retrieves the cached item, possibly populating the cache if it is dirty
        /// or has not been previously populated
        /// </summary>
        public override T GetItem(object populateCacheParameter, params object[] args)
        {
            if (IsCacheValid(populateCacheParameter))
            {
                // If cached item is valid, return the cached item
                return _item;
            }
            else
            {
                // otherwise, populate the cache
                if (_populateCacheDelegate != null || _populateCacheDelegateDirect != null)
                {
                    if (_repopLock == null)
                    {
                        Interlocked.CompareExchange<Lockable>(ref _repopLock, new Lockable(), null);
                    }

                    while (_repopLock.Locked) { Thread.Sleep(10); }

                    // lock so that only one thread can populate the cache at any given time
                    _repopLock.AquireLock();
                    {
                        try
                        {
                            if (!IsCacheValid(populateCacheParameter))
                            {
                                T outval;

                                // Call the populate cache delegate
                                if (_populateCacheDelegateDirect != null)
                                {
                                    Item = _populateCacheDelegateDirect(populateCacheParameter);
                                    return _item;
                                }
                                else if (_populateCacheDelegate(populateCacheParameter, out outval, args))
                                {
                                    Item = outval;
                                    return _item;
                                }
                            }
                        }
                        finally
                        {
                            _repopLock.ReleaseLock();
                        }
                    }
                }
                return default(T);
            }
        }
    }
}
